package dao

import (
	"context"
	"wxpay/internal/model"
	"wxpay/pkg/merrors"
	"wxpay/pkg/util"

	"github.com/nsqio/go-nsq"
	"github.com/patrickmn/go-cache"
	"gorm.io/gorm"
)

type Transfer interface {
	// 创建&更新转账详情
	SaveDetail(ctx context.Context, tx *gorm.DB, m *model.TransferDetail) error
	// 查询转账详情
	QueryDetailByOutDetailNo(ctx context.Context, db *gorm.DB, bizDetailNo string) (t *model.TransferDetail, err error)
	// 批量捞取待生成批次的详情
	QueryAwaitDetails(ctx context.Context, db *gorm.DB, bg string) (tds []*model.TransferDetail, err error)
	// 创建&更新transfer
	SaveTransfer(ctx context.Context, tx *gorm.DB, m *model.Transfer) error
	// 查询转账批次
	QueryTransferByOutBatchNo(ctx context.Context, db *gorm.DB, outBatchNo string) (t *model.Transfer, err error)
	// 根据转账批次号查询详情列表
	QueryDetailsByOutBatchNo(ctx context.Context, db *gorm.DB, outBatchNo string, limit int) (ds []*model.TransferDetail, err error)
	// 批量更新明细单状态
	BatchUpdateDetailStatus(ctx context.Context, tx *gorm.DB, outBatchNo string, status string, outDetailNos []string) error
	// 根据转账详情单号查询转账详情列表
	QueryDetailsByOutDetailNos(ctx context.Context, db *gorm.DB, outDetailNos []string) (ds []*model.TransferDetail, err error)
}

type transferRepo struct {
	*gorm.DB
	producer *nsq.Producer
	cache    *cache.Cache
}

func NewTransferRepo(d *gorm.DB, p *nsq.Producer, c *cache.Cache) *transferRepo {
	return &transferRepo{
		DB:       d,
		producer: p,
		cache:    c,
	}
}

func (*transferRepo) SaveDetail(_ context.Context, tx *gorm.DB, m *model.TransferDetail) error {
	const op merrors.Op = "transferRepo.SaveDetail"
	if e := tx.Save(m).Error; e != nil {
		return merrors.E(op, merrors.DBError, merrors.ErrInternal, e)
	}
	return nil
}

func (*transferRepo) QueryDetailByOutDetailNo(_ context.Context, db *gorm.DB, bizDetailNo string) (t *model.TransferDetail, err error) {
	const op merrors.Op = "transferRepo.QueryDetailByOutDetailNo"
	res := model.TransferDetail{}
	if err := db.Model(&model.TransferDetail{}).Where("biz_detail_no = ?", bizDetailNo).First(&res).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, merrors.E(op, merrors.NotExist, merrors.ErrNotFound, err)
		} else {
			return nil, merrors.E(op, merrors.DBError, merrors.ErrInternal, err)
		}

	}
	t = &res
	return
}

func (r *transferRepo) QueryAwaitDetails(_ context.Context, db *gorm.DB, bg string) (tds []*model.TransferDetail, err error) {
	const op merrors.Op = "transferRepo.QueryAwaitDetails"
	if err = db.Model(&model.TransferDetail{}).Where("batch_group < ? AND status = ? AND out_batch_no IS NULL", bg, model.DetailStatusAwait).Limit(100).Find(&tds).Error; err != nil {
		return nil, merrors.E(op, merrors.DBError, merrors.ErrInternal, err)
	}
	return
}

func (r *transferRepo) SaveTransfer(_ context.Context, tx *gorm.DB, m *model.Transfer) error {
	const op merrors.Op = "transferRepo.SaveTransfer"
	if e := tx.Save(m).Error; e != nil {
		return merrors.E(op, merrors.DBError, e, merrors.ErrInternal)
	}
	return nil
}

func (r *transferRepo) QueryTransferByOutBatchNo(_ context.Context, db *gorm.DB, outBatchNo string) (t *model.Transfer, err error) {
	const op merrors.Op = "transferRepo.QueryTransferByOutBatchNo"
	res := model.Transfer{}
	if err := db.Model(&model.Transfer{}).Where("out_batch_no = ?", outBatchNo).First(&res).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, merrors.E(op, merrors.NotExist, merrors.ErrNotFound, err)
		} else {
			return nil, merrors.E(op, merrors.DBError, merrors.ErrInternal, err)
		}
	}
	t = &res
	return
}

func (r *transferRepo) QueryDetailsByOutBatchNo(_ context.Context, db *gorm.DB, outBatchNo string, limit int) (ds []*model.TransferDetail, err error) {
	const op merrors.Op = "transferRepo.QueryTransferByOutBatchNo"
	if err := db.Model(&model.TransferDetail{}).Where("out_batch_no = ?", outBatchNo).Limit(limit).Find(&ds).Error; err != nil {
		return nil, merrors.E(op, merrors.DBError, merrors.ErrInternal, err)
	}
	return
}

func (r *transferRepo) BatchUpdateDetailStatus(ctx context.Context, tx *gorm.DB, outBatchNo string, status string, outDetailNos []string) (err error) {
	const op merrors.Op = "transferRepo.QueryTransferByOutBatchNo"
	if e := tx.Model(&model.TransferDetail{}).Where("out_batch_no = ? AND out_detail_no IN ?", outBatchNo, outDetailNos).Updates(map[string]interface{}{"status": status, "update_time": util.GetNow()}).Error; e != nil {
		err = merrors.E(op, merrors.DBError, e, merrors.ErrInternal)
		return
	}
	return
}

func (r *transferRepo) QueryDetailsByOutDetailNos(ctx context.Context, db *gorm.DB, outDetailNos []string) (ds []*model.TransferDetail, err error) {
	const op merrors.Op = "transferRepo.QueryDetailsByOutDetailNos"
	if len(outDetailNos) == 0 {
		return
	}
	if err = db.Model(&model.TransferDetail{}).Where("out_detail_no IN ?", outDetailNos).Find(&ds).Error; err != nil {
		return nil, merrors.E(op, merrors.DBError, merrors.ErrInternal, err)
	}
	return
}
